﻿// Partial Copyright (c) LogoUI Software Solutions LTD
// Autor: Vladislav Spivak
// This source file is the part of LogoFX Framework http://logofx.codeplex.com
// See accompanying licences and credits.

//this code is courtesy of http://vortexwolf.wordpress.com/2011/04/09/silverlight-tabcontrol-with-data-binding/
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections;
using System.Collections.Specialized;
using System.Linq;
using System.Windows.Data;

namespace LogoFX.Controls
{
    public class ExtendedTabControl : TabControl
    {
        public ExtendedTabControl()
        {
            this.DefaultStyleKey = typeof(TabControl);
            this.SelectionChanged += OnSelectionChanged;
        }

        #region Tabs with databinding and templates

        /// <summary>
        /// Gets or sets a DataTemplate for a TabItem header
        /// </summary>
        public new DataTemplate ItemTemplate
        {
            get { return (DataTemplate)GetValue(ItemTemplateProperty); }
            set { SetValue(ItemTemplateProperty, value); }
        }
        public new static readonly DependencyProperty ItemTemplateProperty =
            DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(ExtendedTabControl), new PropertyMetadata(
                (sender, e) => ((ExtendedTabControl)sender).InitTabs()));

        /// <summary>
        /// Gets or sets a DataTemplate for a TabItem content
        /// </summary>
        public DataTemplate ContentTemplate
        {
            get { return (DataTemplate)GetValue(ContentTemplateProperty); }
            set { SetValue(ContentTemplateProperty, value); }
        }
        public static readonly DependencyProperty ContentTemplateProperty =
            DependencyProperty.Register("ContentTemplate", typeof(DataTemplate), typeof(ExtendedTabControl), new PropertyMetadata(
                (sender, e) => ((ExtendedTabControl)sender).InitTabs()));

        /// <summary>
        /// Gets or sets a collection used to generate the collection of TabItems
        /// </summary>
        public new IEnumerable ItemsSource
        {
            get { return (IEnumerable)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        public new static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(ExtendedTabControl), new PropertyMetadata(OnItemsSourceChanged));

        private static void OnItemsSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ExtendedTabControl control = (ExtendedTabControl)sender;
            INotifyCollectionChanged incc = e.OldValue as INotifyCollectionChanged;
            if (incc != null)
            {
                incc.CollectionChanged -= control.ItemsSourceCollectionChanged;
            }

            control.InitTabs();

            incc = e.NewValue as INotifyCollectionChanged;
            if (incc != null)
            {
                incc.CollectionChanged += control.ItemsSourceCollectionChanged;
            }
        }


        /// <summary>
        /// Gets or sets the first item in the current selection or returns null if the selection is empty
        /// </summary>
        public new object SelectedItem
        {
            get { return GetValue(SelectedItemProperty); }
            set { SetValue(SelectedItemProperty, value); }
        }

        public new static readonly DependencyProperty SelectedItemProperty =
            DependencyProperty.Register("SelectedItem", typeof(object), typeof(ExtendedTabControl), new PropertyMetadata(OnSelectedItemChanged));

        private static void OnSelectedItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var control = (TabControl)sender; //Base class, not extended, because we must change the original SelectedItem property

            if (e.NewValue == null)
                control.SelectedItem = null;
            else
            {
                var tab = control.Items.Cast<TabItem>().FirstOrDefault(ti => ti.DataContext == e.NewValue);
                if (tab != null && control.SelectedItem != tab)
                    control.SelectedItem = tab;
            }
        }

        /// <summary>
        /// Create the collection of TabItems from the collection of clr-objects
        /// </summary>
        private void InitTabs()
        {
            Items.Clear();
            if (this.ItemsSource == null)
                return;

            foreach (var item in ItemsSource)
            {
                var newitem = this.CreateTabItem(item);
                Items.Add(newitem);
            }
        }

        /// <summary>
        /// Handles the CollectionChanged event of the ItemsSource property
        /// </summary>
        private void ItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                if (e.NewItems != null && e.NewStartingIndex > -1)
                {
                    foreach (var item in e.NewItems.OfType<object>().Reverse())
                    {
                        var newitem = this.CreateTabItem(item);
                        Items.Insert(e.NewStartingIndex, newitem);
                    }
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                if (e.OldStartingIndex > -1)
                    Items.RemoveAt(e.OldStartingIndex);
            }
            else if (e.Action == NotifyCollectionChangedAction.Replace)
            {
                //I don't know how this action can be called. I would rather ignore it.
            }
            else if (e.Action == NotifyCollectionChangedAction.Reset)
            {
                InitTabs();
            }
        }

        /// <summary>
        /// Creates the TabItem object from a clr-object
        /// </summary>
        /// <param name="dataContext">The clr-object which will be set as the DataContext of the TabItem</param>
        /// <returns>The TabItem object</returns>
        private TabItem CreateTabItem(object dataContext)
        {
            var newitem = new TabItem();

            var hca = new Binding("HorizontalContentAlignment") { Source = this };
            BindingOperations.SetBinding(newitem, TabItem.HorizontalContentAlignmentProperty, hca);

            var vca = new Binding("VerticalContentAlignment") { Source = this };
            BindingOperations.SetBinding(newitem, TabItem.VerticalContentAlignmentProperty, vca);

            if (this.ContentTemplate != null)
                newitem.Content = this.ContentTemplate.LoadContent();
            else newitem.Content = dataContext;

            if (this.ItemTemplate != null)
                newitem.Header = this.ItemTemplate.LoadContent();
            else newitem.Header = dataContext;

            newitem.DataContext = dataContext;

            return newitem;
        }

        /// <summary>
        /// Update the current selected clr-item from the selected TabItem
        /// </summary>
        private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var si = e.AddedItems.Cast<TabItem>().FirstOrDefault();
            if (si != null)
                this.SelectedItem = si.DataContext;
            else this.SelectedItem = null;
        }

        #endregion
    }
}
